(*
 *
 *  This file is part of Aws-OCaml
 *
 *  Aws-OCaml is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later 
 *  
 *  Aws-OCaml is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with Aws-OCaml. If not, see <http://www.gnu.org/licenses/>.
 *
 *  command.ml william le ferrand (william@themipsfactory.com) 26/03/2010 09:26
 *
 *)

open Aws_shared.Misc
open Aws_shared.Types
open Aws_shared.Result

module DescribeImages =
  struct 
    let raw_exec connection image_ids = 
      let rec image_list n = function 
	  [] -> [] 
	| t::q -> ((Printf.sprintf "ImageId.%d" n), t) :: (image_list (n+1) q) in

      let request = 
	("AWSAccessKeyId", connection.access_key_id) :: 
	  ("Action", "DescribeImages") ::
	  ("SignatureMethod", "HmacSHA1") ::
	  ("SignatureVersion", "2") ::
	  ("Expires", expiration 180.0) :: 
	  ("Version", "2009-11-30") :: (image_list 1 image_ids) in 
	
    let signature = Aws_shared.Authentication.sign connection request in
    let signature = String.sub signature 0 (String.length signature - 1) in

    let full_request = ("Signature", signature) :: request in
      try 
	Aws_shared.Effector.send connection full_request 
      with Http_client.Http_error (_, s) -> raise (Error s) 


  end

module DescribeInstances = 
  struct 
    let raw_exec connection instance_ids = 
      let rec instance_list n = function 
	  [] -> [] 
	| t::q -> ((Printf.sprintf "InstanceId.%d" n), t) :: (instance_list (n+1) q) in

      let request = 
	("AWSAccessKeyId", connection.access_key_id) :: 
	  ("Action", "DescribeInstances") ::
	  ("SignatureMethod", "HmacSHA1") ::
	  ("SignatureVersion", "2") ::
	  ("Expires", expiration 180.0) :: 
	  ("Version", "2009-11-30") :: (instance_list 1 instance_ids) in 
	
    let signature = Aws_shared.Authentication.sign connection request in
    let signature = String.sub signature 0 (String.length signature - 1) in

    let full_request = ("Signature", signature) :: request in
      try 
	Aws_shared.Effector.send connection full_request 
      with Http_client.Http_error (_, s) -> raise (Error s) 

    let exec connection instance_ids = 
      let result = raw_exec connection instance_ids in 
	debug "@@@ %s\n" result; 
	let instance_set = match Xml.parse_string result with
	    Xml.Element (_, _, Xml.Element (_,_,_) :: Xml.Element (_,_,reservationset) :: _) ->
	      
	      List.fold_left (fun acc e -> 
				match e with 
				    Xml.Element (_, _,  Xml.Element (_,_,_) ::  Xml.Element (_,_,_)  ::  Xml.Element (_,_,_) ::  Xml.Element (_,_,instanceset::_)  :: _ ) -> instanceset :: acc
				  | _ -> acc )
		[] 
		reservationset 
		
	| _ -> raise Malformed in

	  
	List.map (function 
		      Xml.PCData _ -> 
			display "@@@ Panic, in Xml.PCData\n";
			{ label = "panic" ; attributes = [] }
		    | Xml.Element (ll, _, attributes) -> 
			display "@@@ Lablel %s\n" ll ; 
			let attrs = List.map (function 
						  Xml.Element (name, _, Xml.PCData (value) :: _) -> 
						    { name = name; value = value } 
						| _  -> 
						    { name = "panic"; value = "" })  attributes in
			  
			let instance_id = List.find (fun e -> e.name = "instanceId") attrs in
			  { label = instance_id.value ; attributes = attrs }) 
	  instance_set

  end

module RunInstances = 
  struct 
    let raw_exec connection image_id min_count max_count instance_type key_name user_data = 

      let encoder = Cryptokit.Base64.encode_compact () in 
      let user_data_encoded = Cryptokit.transform_string encoder (user_data^"   ") in

      let request = 
	("AWSAccessKeyId", connection.access_key_id) :: 
	  ("Action", "RunInstances") ::
	  ("DisableApiTermination", "false") ::
	  ("SignatureMethod", "HmacSHA1") ::
	  ("SignatureVersion", "2") ::
	  ("Expires", expiration 60.0) :: 
	  ("InstanceType", instance_type) :: 
	  ("MaxCount", (string_of_int max_count)) :: 
	  ("MinCount", (string_of_int min_count)) :: 
	  ("ImageId", image_id) ::
	  ("UserData", user_data_encoded) ::
	  ("KeyName", key_name) ::
	  ("Version", "2009-11-30") :: [] in 
    
	let signature = Aws_shared.Authentication.sign connection request in
	let signature = String.sub signature 0 (String.length signature - 1) in
	  
	let full_request = ("Signature", signature) :: request in
	  try 
	    Aws_shared.Effector.send connection full_request 
	  with Http_client.Http_error (_, s) -> raise (Error s)

    let extract result = 
      let response, _ = split_result result in 
	List.map (function 
		      Xml.Element (_, _, attribute) -> 
			(match attribute with 
			     Xml.Element (_,_, Xml.PCData (name) :: _) :: 
			       Xml.Element (_,_, Xml.PCData (value) :: _) :: _ -> 
				 { name = name; value = value }
			   | _ -> raise Malformed)
		    | _ -> raise Malformed) response 

    let exec connection image_id min_count max_count instance_type key_name user_data = 
      let result = raw_exec connection image_id min_count max_count instance_type key_name user_data in 
	display "@@@ Result: %s\n" result; 
      let instance_set = 
	match Xml.parse_string result with
	    Xml.Element (_, _, Xml.Element (_,_,_) :: Xml.Element (_,_,_) ::Xml.Element (_,_,_) ::Xml.Element (_,_,_) :: Xml.Element (_,_,instanceset) :: _) -> 
	      instanceset
	  | _ -> raise Malformed in
	
	List.iter (fun e -> display "@@@\n%s\n" (Xml.to_string_fmt e)) instance_set ;

	display "\n---\n"; 
	List.map (function 
		      Xml.PCData _ -> 
			display "@@@ Panic, in Xml.PCData\n";
			{ label = "panic" ; attributes = [] }
		    | Xml.Element (ll, _, attributes) -> 
			display "@@@ Lablel %s\n" ll ; 
			let attrs = List.map (function 
						 Xml.Element (name, _, Xml.PCData (value) :: _) -> 
						   { name = name; value = value } 
						| _  -> 
						    { name = "panic"; value = "" })  attributes in

			let instance_id = List.find (fun e -> e.name = "instanceId") attrs in
			  { label = instance_id.value ; attributes = attrs }) instance_set
  end


module TerminateInstances =
  struct 
    let raw_exec connection instance_ids = 
      let rec instance_list n = function 
	  [] -> [] 
	| t::q -> ((Printf.sprintf "InstanceId.%d" n), t) :: (instance_list (n+1) q) in
	
      let request = 
	("AWSAccessKeyId", connection.access_key_id) :: 
	  ("Action", "TerminateInstances") ::
	  ("SignatureMethod", "HmacSHA1") ::
	  ("SignatureVersion", "2") ::
	  ("Expires", expiration 60.0) :: 
	  ("Version", "2009-11-30") :: (instance_list 1 instance_ids) in 
	
      let signature = Aws_shared.Authentication.sign connection request in
      let signature = String.sub signature 0 (String.length signature - 1) in
	
      let full_request = ("Signature", signature) :: request in
	try 
	  Aws_shared.Effector.send connection full_request 
	with Http_client.Http_error (_, s) -> raise (Error s)
  end


module LocalDetails = 
  struct 
    let local_ipv4 () = 
      "http://169.254.169.254/latest/user-data/local-ipv4" >>> Http_client.Convenience.http_get 
	
    let instance_id () = 
      "http://169.254.169.254/latest/user-data/instance-id" >>> Http_client.Convenience.http_get 

  end
  

